package comp353Games;

import java.awt.Dimension;
import java.awt.BorderLayout;
import java.awt.event.*;
import java.io.File;
import java.util.Formatter;
import java.util.Scanner;
import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.filechooser.FileNameExtensionFilter;

/**
 * GameController coordinates between Game Models and Game Displays. All games
 * and displays use this Controller
 *
 * @author Tyler Radley, Andre Harvey, Deanna Fink
 * @version 4/30/12
 */
public class GameController extends JFrame implements MouseListener {

    //An instance of the current game
    private GameModel[] game;
    //The x and y coordinate where the mouse was pressed
    private int[] gamePress = new int[2];
    //The x and y coordinate where the mouse was released
    private int[] gameRelease = new int[2];
    //The x and y coordinate where the mouse was clicked
    private int[] gameClick = new int[2];
    // The JFrame we are displaying the game in.
    private JFrame frame;
    //An array containing the two JPanels that will hold the games being played
    private JPanel[] gamePanel = new JPanel[]{new JPanel(), new JPanel()};
    //The JLabels we are displaying the status of the games in.
    private JLabel[] statusBar = new JLabel[]{new JLabel(), new JLabel()};
    //The current player for any of the games currently being played
    private static int currentPlayer;
    //The current game being played
    private static int currentGame;
    public boolean runOnce = false;
    /*
     * The number of players playing the game
     */
    public static final int NUM_PLAYERS = 2;

    /**
     * Constructor for the general controller that sets the initial attributes
     * of the JFrame and starts the game.
     */
    public void GameController() {
        String gameName = "";
        for (int i = 0; i < game.length; i++) {
            gameName += game[i].getName();
            if (i == 0 && (game.length == 2)) {
                gameName += " & ";
            }
        }

        this.frame = new JFrame(gameName);
        this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.frame.setPreferredSize(new Dimension(1000 + (280 * (game.length - 1)), 600));
        JMenuBar menuBar = this.createMenuBar();
        frame.setJMenuBar(menuBar);

        for (int i = 0; i < game.length; i++) {
            this.gamePanel[i].setMinimumSize(new Dimension(1000, 500));
            this.gamePanel[i].setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
            this.gamePanel[i].addMouseListener(this);
            this.statusBar[i] = new JLabel(game[i].getInstructions(currentPlayer)[0]
                    + game[i].getInstructions(currentPlayer)[1], SwingConstants.CENTER);

        }
        this.frame.add(statusBar[0], BorderLayout.PAGE_START);
        this.frame.add(gamePanel[0], BorderLayout.LINE_START);



        this.gamePanel[0].addMouseListener(new java.awt.event.MouseAdapter() {

            public void mouseEntered(java.awt.event.MouseEvent event) {
                currentGame = 0;
                gamePanelMouseEntered(event);
            }
        });
        if (game.length > 1) {
            this.frame.add(statusBar[1], BorderLayout.PAGE_END);
            this.frame.add(gamePanel[1], BorderLayout.LINE_END);
            this.gamePanel[1].addMouseListener(new java.awt.event.MouseAdapter() {

                public void mouseEntered(java.awt.event.MouseEvent event) {
                    currentGame = 1;
                    gamePanelMouseEntered(event);
                }
            });
        }

        //Display the window.
        frame.setLocationRelativeTo(null);
        frame.pack();
        frame.setVisible(true);
    }

    /**
     * Class constructor for generic game.
     *
     * @param game game to begin with.
     */
    public GameController(GameModel[] game) {
        this.game = game;
        currentPlayer = 1;
        if (runOnce) {
            this.displayGame();
        }
        runOnce = true;
    }

    /**
     * Sets up a JMenuBar that will be used to restart the game or choose a
     * different game
     *
     * @return The JMenuBar that will be placed at the top of the window
     */
    public JMenuBar createMenuBar() {
        JMenu fileMenu = new JMenu("File");
        JMenuBar menuBar = new JMenuBar();
        menuBar.add(fileMenu);
        JMenuItem newGameMenuItem;
        JMenuItem gameChooserMenuItem;
        JMenuItem saveGameMenuItem;
        JMenuItem loadGameMenuItem;
        newGameMenuItem = new JMenuItem("New Game", KeyEvent.VK_T);
        newGameMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
        newGameMenuItem.getAccessibleContext().setAccessibleDescription(
                "Starts a new Game");

        newGameMenuItem.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent event) {
                newGameActionPerformed(event);
            }
        });

        saveGameMenuItem = new JMenuItem("Save", KeyEvent.VK_T);
        saveGameMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
        saveGameMenuItem.getAccessibleContext().setAccessibleDescription(
                "Saves a selected game");

        saveGameMenuItem.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent event) {
                saveGameActionPerformed(event);
            }
        });

        loadGameMenuItem = new JMenuItem("Load", KeyEvent.VK_T);
        loadGameMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
        loadGameMenuItem.getAccessibleContext().setAccessibleDescription(
                "Loads a game");

        loadGameMenuItem.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent event) {
                loadGameActionPerformed(event, false, false);
            }
        });

        gameChooserMenuItem = new JMenuItem("Game Chooser", KeyEvent.VK_T);
        gameChooserMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
        gameChooserMenuItem.getAccessibleContext().setAccessibleDescription(
                "Opens the Game Choice Menu");

        gameChooserMenuItem.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(java.awt.event.ActionEvent event) {
                chooseGameActionPerformed(event);
            }
        });
        fileMenu.add(newGameMenuItem);
        fileMenu.add(saveGameMenuItem);
        fileMenu.add(loadGameMenuItem);
        fileMenu.add(gameChooserMenuItem);
        this.displayGame();
        return menuBar;
    }

    /**
     * Creates a display for the game.
     */
    public void displayGame() {
        for (int i = 0; i < game.length; i++) {
            this.gamePanel[i].removeAll();
            this.gamePanel[i].add(game[i].getDisplay(this.game[i]));
        }
        this.frame.setVisible(true);
        this.frame.pack();
        this.frame.repaint();
    }

    //Prepares the graphical changes of the panel on which the mouse points
    private void gamePanelMouseEntered(java.awt.event.MouseEvent event) {
        for (int i = 0; i < game.length; i++) {
            if (game[currentGame].hasMovesRemaining(currentPlayer)) {
                gamePanel[currentGame].setBackground(new java.awt.Color(242 - (currentGame * 8), 234
                        + (currentGame * 8), 239 + (currentGame * 5)));
                gamePanel[1 - currentGame].setBackground(new java.awt.Color(235, 235, 235));
            } else {
                gamePanel[currentGame].setBorder(javax.swing.BorderFactory.createEtchedBorder(java.awt.Color.WHITE, java.awt.Color.LIGHT_GRAY));
            }
            statusBar[1 - currentGame].setBackground(new java.awt.Color(235, 235, 235));
            statusBar[currentGame].setBackground(new java.awt.Color(242 - (currentGame * 57), 208
                    + (currentGame * 41), 237 - (currentGame * 52)));
            statusBar[currentGame].setOpaque(true);
        }
    }

    @Override
    public void mouseExited(java.awt.event.MouseEvent event) {
        if (game.length == 1) {
            gamePanel[0].setBackground(new java.awt.Color(235, 235, 235));
            statusBar[0].setBackground(new java.awt.Color(235, 235, 235));
        }
    }

    @Override
    public void mousePressed(java.awt.event.MouseEvent event) {
        this.gamePress = game[currentGame].mousePressedHandler(event.getX(), event.getY());
    }

    @Override
    public void mouseReleased(java.awt.event.MouseEvent event) {
        this.gameRelease = game[currentGame].mouseReleasedHandler(event.getX(), event.getY());
        if (gameRelease != null) {
            move();
        }
    }

    @Override
    public void mouseClicked(java.awt.event.MouseEvent event) {
        this.gameClick = new int[2];
        this.gameClick = game[currentGame].mouseClickedHandler(event.getX(), event.getY(), currentPlayer);
        if (gameClick != null && gameClick[0] != -1 && gameClick[1] != -1) {
            move();
        }
    }

    /**
     * If the move is valid, moves to a new game and checks to see if there are
     * any more moves.
     */
    public void move() {
        String gameDone = "There are currently no availible moves in this game.";
        if (((gamePress != null && gameRelease != null) || gameClick != null)
                && game[currentGame].validMove(gamePress, gameRelease, gameClick, currentPlayer)) {
            this.game[currentGame] = game[currentGame].updateGame(gamePress, gameRelease, gameClick, currentPlayer);
            currentPlayer = currentPlayer % NUM_PLAYERS + 1;
            if (game.length > 1 && game[currentGame].hasMovesRemaining(currentPlayer) && game[1 - currentGame].hasMovesRemaining(currentPlayer)) {
                for (int i = 0; i < game.length; i++) {
                    statusBar[i].setText(game[i].getInstructions(currentPlayer)[1]);
                    gamePanel[i].setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
                }
                this.displayGame();
            } else if (game[currentGame].hasMovesRemaining(currentPlayer)) {
                if (game.length > 1) {
                    statusBar[1 - currentGame].setText(gameDone);
                }
                statusBar[0].setText(game[0].getInstructions(currentPlayer)[1]);
            } else if (game.length > 1 && game[1 - currentGame].hasMovesRemaining(currentPlayer)) {
                if (game.length > 1) {
                    gamePanel[1 - currentGame].setBackground(new java.awt.Color(242 - ((1 - currentGame) * 57), 208 + ((1 - currentGame) * 41), 237 - ((1 - currentGame) * 52)));
                    statusBar[1 - currentGame].setText(game[1 - currentGame].getInstructions(currentPlayer)[1]);
                }
                gamePanel[currentGame].setBorder(javax.swing.BorderFactory.createEtchedBorder(java.awt.Color.WHITE, java.awt.Color.LIGHT_GRAY));
                gamePanel[currentGame].setBackground(new java.awt.Color(235, 235, 235));
                statusBar[currentGame].setText(gameDone);
            } else {
                this.displayGame();
                for (int i = 0; i < game.length; i++) {
                    statusBar[i].setText("Sorry, " + game[i].getInstructions(currentPlayer)[2] + ", there are no more moves for you, you lose. Start a new game or return to the Game Chooser.");
                    gamePanel[i].setBorder(javax.swing.BorderFactory.createEtchedBorder(new java.awt.Color(234, 242, 234), java.awt.Color.LIGHT_GRAY));
                    gamePanel[i].setBackground(new java.awt.Color(235, 235, 235));
                }
            }
            this.displayGame();
        }
    }

    /**
     * Returns a string identifying the current player's token in each game.
     *
     * @return the tokens of the current player separated by a slash.
     */
    public String getPlayerToken() {
        String playerToken = "";
        for (int i = 0; i < game.length; i++) {
            playerToken += game[i].getInstructions(currentPlayer)[2];
            if (i == 0 && game.length > 1) {
                playerToken += "/";
            }
        }
        return playerToken;
    }

    /**
     * Returns a string version of this.
     *
     * @param indent Indentation string.
     * @return //An empty string.
     */
    public String toString(String indent) {
        String string = "";
        return string;
    }

    @Override
    public String toString() {
        return this.toString("");
    }

    //Defines what happens when new game is selected from the file menu.
    private void newGameActionPerformed(java.awt.event.ActionEvent event) {
        Object[] options = {"Yes", "No, return to the game"};
        String resignNew;
        if (game[0].hasMovesRemaining(currentPlayer) || (game.length > 1 && game[1].hasMovesRemaining(currentPlayer))) {
            resignNew = getPlayerToken() + " are you sure you want to resign and start a new game?";
        } else {
            resignNew = "Are you sure you want to start a new game?";
        }
        int n = JOptionPane.showOptionDialog(frame, resignNew, "",
                JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE,
                null, options, options[1]);
        if (n == 0) {
            this.frame.dispose();
            new Thread(new Runnable() {

                public void run() {
                    for (int i = 0; i < game.length; i++) {
                        game[i] = game[i].getNewGame();
                    }
                    GameController controller = new GameController(game);
                    controller.GameController();
                }
            }).start();
        }
    }

    //Defines what happens when the game chooser option is selected from the file menu
    private void chooseGameActionPerformed(java.awt.event.ActionEvent event) {
        Object[] options = {"Yes", "No, return to the game"};
        String resignNew;
        if (game[0].hasMovesRemaining(currentPlayer) || (game.length > 1 && game[1].hasMovesRemaining(currentPlayer))) {
            resignNew = getPlayerToken() + " are you sure you want to resign this game and return to the game chooser?";
        } else {
            resignNew = "Are you sure you want to start a return to the game chooser?";
        }
        int n = JOptionPane.showOptionDialog(frame, resignNew,
                "", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE,
                null, options, options[1]);
        if (n == 0) {
            this.frame.dispose();
            new Thread(new Runnable() {

                public void run() {
                    Opener.main(null);
                }
            }).start();
        }
    }

    //Saves the selected game to be played later
    private void saveGameActionPerformed(java.awt.event.ActionEvent event) {
        String secondGameName;
        Formatter formatter = null;
        String saveGame;
        JFileChooser chooser = new JFileChooser();
        FileNameExtensionFilter filter = new FileNameExtensionFilter(".txt", "txt");
        chooser.setFileFilter(filter);
        if (game.length > 1) {
            secondGameName = game[1].getName();
        } else {
            secondGameName = "EMPTY";
        }
        Object[] options = {"Game 1 (" + game[0].getName() + ")", "Game 2 (" + secondGameName + ")", "Cancel"};
        String saveChooser = "Which game summand would you like to save?";
        int targetGame = 0;
        if (game.length > 1) {
            targetGame = JOptionPane.showOptionDialog(frame, saveChooser, "", JOptionPane.YES_NO_CANCEL_OPTION,
                    JOptionPane.INFORMATION_MESSAGE, null, options, options[1]);
        }
        int returnVal = chooser.showSaveDialog(null);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            saveGame = game[targetGame].save();
            try {
                formatter = new Formatter(chooser.getSelectedFile().getPath());
            } catch (Exception e) {
                System.out.println("you have an error.");
            }
            formatter.format(saveGame);
            formatter.close();
        }
    }

    /**
     * Creates a game based on the selected save file
     *
     * @param event the event associated with the pressing of the load button.
     */
    public void loadGameActionPerformed(java.awt.event.ActionEvent event, boolean twoLoads, boolean openerLoadButtonPressed) {
        JFileChooser chooser = new JFileChooser();
        Scanner scanner = null;
        int targetGame = 0;
        FileNameExtensionFilter filter = new FileNameExtensionFilter(".txt", "txt");
        chooser.setFileFilter(filter);
        int returnVal = chooser.showSaveDialog(null);
        String secondGameName = "";
        if (game.length > 1 && !openerLoadButtonPressed) {
            secondGameName = game[1].getName();
        } else {
            secondGameName = "EMPTY";
        }

        Object[] options;
        String saveChooser = "Where would you like to load this summand to?";

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            if (game.length > 0 && !openerLoadButtonPressed) {
                options = new Object[]{"Game 1 (" + game[0].getName() + ")", "Game 2 (" + secondGameName + ")", "Cancel"};
                targetGame = JOptionPane.showOptionDialog(frame, saveChooser, "", JOptionPane.YES_NO_CANCEL_OPTION,
                        JOptionPane.INFORMATION_MESSAGE, null, options, options[1]);
            }
            try {
                scanner = new Scanner(new File(chooser.getSelectedFile().getPath()));
            } catch (Exception e) {
                System.out.println("could not find file");
            }
            String loadString = "";

            while (scanner.hasNext()) {
                loadString += scanner.next();
            }

            scanner.close();

            System.out.println(loadString);

            parseLoadedFile(loadString, targetGame, openerLoadButtonPressed, twoLoads);
        }
    }
    //Identfies and runs a saved game using the specific game's load method.

    private void parseLoadedFile(String loadString, int targetGame, boolean openerLoadButtonPressed, boolean twoLoads) {
        GameModel[] gameSum;
        GameModel loadHelper = null;
        int index = 0;
        String gameType = "";
        while (loadString.charAt(index) != '<' && index < 10) {
            index++;
        }
        if (index != 10) {
            index++;
            while (loadString.charAt(index) != '>') {
                gameType += loadString.charAt(index);
                index++;
            }
            index++;

        }
        if (gameType.equals("Clobber")) {
            loadHelper = new Clobber();

        }
        if (gameType.equals("Snort")) {
            loadHelper = new SnortCol(true);

        }
        if (gameType.equals("Col")) {
            loadHelper = new SnortCol(false);

        }
        if (gameType.equals("Nim")) {
            loadHelper = new Nim();

        }
        if (gameType.equals("ToadsAndFrogs")) {
            loadHelper = new ToadsAndFrogs();

        }

        if (game.length == 1) {
            gameSum = new GameModel[]{game[0], loadHelper.load(loadString)};
            game = gameSum;
        } else {
            if (game.length == 0) {
                game = new GameModel[1];
            }
            if (openerLoadButtonPressed && !twoLoads) {
                targetGame = 1;
            }
            this.game[targetGame] = loadHelper.load(loadString);

        }

        GameController controller = new GameController(game);
        controller.GameController();
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }
}